#include <file_manager.h>
#include <transmitter.h>
#include <cstdlib>
#include <iostream>
#include <boost/bind.hpp>
#include <boost/asio.hpp>

using boost::asio::ip::tcp;
namespace bap = boost::asio::placeholders;

class session {
    /*
     * handle one client. Examine and execute commands from
     * the clients, and return the result.
     * TODO: It is only an echo server now.
     */
public:
    session(boost::asio::io_service & io_service):
            socket_(io_service) {
    }
    ~session() {
        std::cerr << "a client quited" << std::endl;
    }

    tcp::socket & socket() {
        return socket_;
    }

    void
    start() {
        boost::asio::async_read_until(socket_, buf, "\r\n",
                                      boost::bind(&session::handle_read,
                                                  this,
                                                  bap::error,
                                                  bap::bytes_transferred));
    }

    void
    handle_read(const boost::system::error_code & error,
                size_t bytes_transferred) {
        if (!error) {
            boost::asio::async_write(socket_, buf,
                                     boost::bind(&session::handle_write,
                                                 this,
                                                 bap::error));
        } else {
            delete this;
        }
    }

    void
    handle_write(const boost::system::error_code & error) {
        if (!error) {
            boost::asio::async_read_until(socket_, buf, "\r\n",
                                          boost::bind(&session::handle_read,
                                                      this,
                                                      bap::error,
                                                      bap::bytes_transferred));

        } else {
            delete this;
        }
    }

private:
    tcp::socket socket_;
    boost::asio::streambuf buf;
};

class server {
    /*
     * Bind and accept connection request from the clients.
     * it will create one session object for each client.
     */
public:
    server(boost::asio::io_service & io_service, short port)
            :io_service_(io_service),
            acceptor_(io_service, tcp::endpoint(tcp::v4(),port)) {
        session *new_session = new session(io_service_);
        acceptor_.async_accept(new_session->socket(),
                               boost::bind(&server::handle_accept,
                                           this,
                                           new_session,
                                           bap::error));
    }
    void
    handle_accept(session * new_session,
                  const boost::system::error_code & error) {
        if (!error) {
            new_session->start();
            new_session = new session(io_service_);
            acceptor_.async_accept(new_session->socket(),
                                   boost::bind(&server::handle_accept,
                                               this,
                                               new_session,
                                               bap::error));
        } else {
            delete new_session;
        }
    }

private:
    boost::asio::io_service & io_service_;
    tcp::acceptor acceptor_;
};

int main(int argc, char *argv[]) {
    if (argc != 2) {
        std::cerr << "Usage: " << argv[0] << " <port>" << std::endl;;
        return -1;
    }

    try {

        boost::asio::io_service io_service;

        server s(io_service, std::atoi(argv[1]));

        io_service.run();
    } catch (std::exception & e) {
        std::cerr << e.what() << std::endl;
    }

    return 0;
}
